/*
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef FFTOOLS_FFMPEG_H
#define FFTOOLS_FFMPEG_H

#include "config.h"

#include <stdint.h>
#include <stdio.h>
#include <signal.h>

#include "cmdutils.h"

#include "libavformat/avformat.h"
#include "libavformat/avio.h"

#include "libavcodec/avcodec.h"

#include "libavfilter/avfilter.h"

#include "libavutil/avutil.h"
#include "libavutil/dict.h"
#include "libavutil/eval.h"
#include "libavutil/fifo.h"
#include "libavutil/hwcontext.h"
#include "libavutil/pixfmt.h"
#include "libavutil/rational.h"
#include "libavutil/thread.h"
#include "libavutil/threadmessage.h"

#include "libswresample/swresample.h"

#define VSYNC_AUTO       -1  
//代表不改变视频的存储方式，也就是不会改变视频帧的 pts，输入是怎样的时间，输出还是怎样的时间 
#define VSYNC_PASSTHROUGH 0  
//代表强制输出流为恒定帧率模式，CFR 的全称是 constant frame rate （恒定帧率）。
//恒定帧率就是说，如果是 1/24 的帧率，那一秒钟肯定有 24 帧数据保存在文件里面
#define VSYNC_CFR         1  
//与恒定帧率模式对应的就是可变帧率模式，VFR 的全称是 variable frame rate （可变帧率）。可变帧率可以让文件体积变小
#define VSYNC_VFR         2 
//从 CFR 转换过来的，有两种情况会 从 CFR 换成 VSCFR
//1.文件只有一个输入流，而且命令没有使用 -itsoffset 选项
//2.命令行使用了 -copyts 选项
#define VSYNC_VSCFR       0xfe
// 跟 VSYNC_PASSTHROUGH非常类似，但是会在write_packet()里把 pts,dts 破坏掉，这样av_interleaved_write_frame() 
// 内部会按照帧率来设置 pts
#define VSYNC_DROP        0xff 

#define MAX_STREAMS 1024    /* arbitrary sanity check value */

enum HWAccelID {
    HWACCEL_NONE = 0,
    HWACCEL_AUTO,
    HWACCEL_GENERIC,
    HWACCEL_VIDEOTOOLBOX,
    HWACCEL_QSV,
};

typedef struct HWAccel {
    const char *name;
    int (*init)(AVCodecContext *s);
    enum HWAccelID id;
    enum AVPixelFormat pix_fmt;
} HWAccel;

typedef struct HWDevice {
    const char *name;
    enum AVHWDeviceType type;
    AVBufferRef *device_ref;
} HWDevice;

/* select an input stream for an output stream */
typedef struct StreamMap {
    int disabled;           /* 1 is this mapping is disabled by a negative map */
    int file_index;
    int stream_index;
    int sync_file_index;
    int sync_stream_index;
    char *linklabel;       /* name of an output link, for mapping lavfi outputs */
} StreamMap;

typedef struct {
    int  file_idx,  stream_idx,  channel_idx; // input
    int ofile_idx, ostream_idx;               // output
} AudioChannelMap;

//保存的是单个输入或者输出文件的信息
typedef struct OptionsContext {
    OptionGroup *g; //OptionsContext 本身就是从 OptionGroup 里面的 Option 解析过来，
    //这里又保存一下 OptionGroup ，是因为要用到里面的 codec_opts ，format_opts，这些组件库相关的参数，
    //是没有解析到 OptionsContext 里面的

    /* input/output options */
    int64_t start_time;
    int64_t start_time_eof;
    int seek_timestamp;
    const char *format;

    SpecifierOpt *codec_names; //编解码器名称
    int        nb_codec_names;
    SpecifierOpt *audio_channels;
    int        nb_audio_channels;
    SpecifierOpt *audio_sample_rate;
    int        nb_audio_sample_rate;
    SpecifierOpt *frame_rates;
    int        nb_frame_rates;
    SpecifierOpt *frame_sizes;
    int        nb_frame_sizes;
    SpecifierOpt *frame_pix_fmts;
    int        nb_frame_pix_fmts;

    /* input options */
    int64_t input_ts_offset;
    int loop;
    int rate_emu;
    int accurate_seek;
    int thread_queue_size;

    SpecifierOpt *ts_scale;
    int        nb_ts_scale;
    SpecifierOpt *dump_attachment;
    int        nb_dump_attachment;
    SpecifierOpt *hwaccels;
    int        nb_hwaccels;
    SpecifierOpt *hwaccel_devices;
    int        nb_hwaccel_devices;
    SpecifierOpt *hwaccel_output_formats;
    int        nb_hwaccel_output_formats;
    SpecifierOpt *autorotate;
    int        nb_autorotate;

    /* output options */
    StreamMap *stream_maps;
    int     nb_stream_maps;
    AudioChannelMap *audio_channel_maps; /* one info entry per -map_channel */
    int           nb_audio_channel_maps; /* number of (valid) -map_channel settings */
    int metadata_global_manual;
    int metadata_streams_manual;
    int metadata_chapters_manual;
    const char **attachments;
    int       nb_attachments;

    int chapters_input_file;

    int64_t recording_time;
    int64_t stop_time;
    uint64_t limit_filesize;
    float mux_preload;
    float mux_max_delay;
    int shortest;
    int bitexact;

    int video_disable;
    int audio_disable;
    int subtitle_disable;
    int data_disable;

    /* indexed by output file stream index */
    int   *streamid_map;
    int nb_streamid_map;
    //数组字段
    SpecifierOpt *metadata; //数组指针
    int        nb_metadata; //数组长度
    SpecifierOpt *max_frames;
    int        nb_max_frames;
    SpecifierOpt *bitstream_filters;
    int        nb_bitstream_filters;
    SpecifierOpt *codec_tags;
    int        nb_codec_tags;
    SpecifierOpt *sample_fmts;
    int        nb_sample_fmts;
    SpecifierOpt *qscale;
    int        nb_qscale;
    SpecifierOpt *forced_key_frames;
    int        nb_forced_key_frames;
    SpecifierOpt *force_fps;
    int        nb_force_fps;
    SpecifierOpt *frame_aspect_ratios;
    int        nb_frame_aspect_ratios;
    SpecifierOpt *rc_overrides;
    int        nb_rc_overrides;
    SpecifierOpt *intra_matrices;
    int        nb_intra_matrices;
    SpecifierOpt *inter_matrices;
    int        nb_inter_matrices;
    SpecifierOpt *chroma_intra_matrices;
    int        nb_chroma_intra_matrices;
    SpecifierOpt *top_field_first;
    int        nb_top_field_first;
    SpecifierOpt *metadata_map;
    int        nb_metadata_map;
    SpecifierOpt *presets;
    int        nb_presets;
    SpecifierOpt *copy_initial_nonkeyframes;
    int        nb_copy_initial_nonkeyframes;
    SpecifierOpt *copy_prior_start;
    int        nb_copy_prior_start;
    SpecifierOpt *filters;
    int        nb_filters;
    SpecifierOpt *filter_scripts;
    int        nb_filter_scripts;
    SpecifierOpt *reinit_filters;
    int        nb_reinit_filters;
    SpecifierOpt *fix_sub_duration;
    int        nb_fix_sub_duration;
    SpecifierOpt *canvas_sizes;
    int        nb_canvas_sizes;
    SpecifierOpt *pass;
    int        nb_pass;
    SpecifierOpt *passlogfiles;
    int        nb_passlogfiles;
    SpecifierOpt *max_muxing_queue_size;
    int        nb_max_muxing_queue_size;
    SpecifierOpt *guess_layout_max;
    int        nb_guess_layout_max;
    SpecifierOpt *apad;
    int        nb_apad;
    SpecifierOpt *discard;
    int        nb_discard;
    SpecifierOpt *disposition;
    int        nb_disposition;
    SpecifierOpt *program;
    int        nb_program;
    SpecifierOpt *time_bases;
    int        nb_time_bases;
    SpecifierOpt *enc_time_bases;
    int        nb_enc_time_bases;
} OptionsContext;

typedef struct InputFilter {
    //滤镜上下文，我把它称为滤镜实例，这个字段肯定是一个 buffer入口滤镜。是通过 avfilter_graph_create_filter() 创建的
    AVFilterContext    *filter;
    //关联的输入流，这个入口滤镜的 AVFrame 是从哪个输入流输入的。这里注意，多个 InputFilter 可能是由同一个输入流来输入的
    struct InputStream *ist;
    //关联的 FilterGraph
    struct FilterGraph *graph;
    //名称，通过 describe_filter_link() 计算出来的
    uint8_t            *name;
    //数据类型，可以是视频，音频，或者字幕。但是复杂滤镜目前不支持字幕
    enum AVMediaType    type;   // AVMEDIA_TYPE_SUBTITLE for sub2video

    //AVFrame 的缓存队列，当输入流解码出来 AVFrame，但是还不能调 av_buffersrc_add_frame_flags() 发往滤镜，
    //就会先缓存在 frame_queue 里面
    AVFifoBuffer *frame_queue;

    // parameters configured for this input
    //格式，输入流 AVFrame 的格式
    int format;

    int width, height;
    AVRational sample_aspect_ratio;

    int sample_rate;
    int channels;
    uint64_t channel_layout;

    AVBufferRef *hw_frames_ctx;

    int eof;
} InputFilter;

typedef struct OutputFilter {
    //滤镜上下文，这个字段肯定是一个 buffersink 出口滤镜。是通过 avfilter_graph_create_filter() 创建的
    AVFilterContext     *filter; 
    struct OutputStream *ost; //关联的输出流
    struct FilterGraph  *graph; //关联的 FilterGraph
    uint8_t             *name; //名称

    /* temporary storage until stream maps are processed */
    AVFilterInOut       *out_tmp;
    enum AVMediaType     type;

    /* desired output stream properties */
    int width, height;
    AVRational frame_rate;
    int format;
    int sample_rate;
    uint64_t channel_layout;

    // those are only set if no format is specified and the encoder gives us multiple options
    int *formats;
    uint64_t *channel_layouts;
    int *sample_rates;
} OutputFilter;

//滤镜容器
typedef struct FilterGraph {
    //全局变量 filtergraphs 数组里的索引，ffmpeg.exe 里面是可以有多个 FilterGraph 的，
    //例如音频的 FilterGraph ，视频的 FilterGraph
    int            index;
    //命令行选项 -filter_complex 的值，如果是简单滤镜，这个字段是个空字符串
    const char    *graph_desc;

    AVFilterGraph *graph; //FFmpeg 的数据结构，滤镜容器
    int reconfiguration;

    InputFilter   **inputs; //buffer 入口滤镜数组，入口滤镜可以有多个
    int          nb_inputs;
    OutputFilter **outputs; //buffersink 出口滤镜数组,，出口滤镜可以有多个
    int         nb_outputs;
} FilterGraph;

//单个输入流的管理器
typedef struct InputStream {
    int file_index;  //input_files 数组里面的下标，代表 InputStream 对应的 InputFile
    AVStream *st; //AVFormatContext 里面的 AVStream
    //如果是 1 会丢弃读取到的 AVPacket，刚开始的时候都是 1。
    //例如文件里面有多个视频流，会全部都把 discard 设置为 1，然后再从中选出质量最好的视频流，把它的 discard 设置为 0
    //或者当输出流需要这个输入流的时候，也会置为 0 
    int discard;             /* true if stream data should be discarded */
    //命令行选项 -discard 的值，默认是 AVDISCARD_NONE，可选的值在 AVDiscard 枚举里面
    int user_set_discard;
    //大于 0 代表输入流需要进行解码操作，有两个值，DECODING_FOR_OST ，DECODING_FOR_FILTER
    int decoding_needed;     /* non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */
#define DECODING_FOR_OST    1
#define DECODING_FOR_FILTER 2

    //解码器上下文/解码器实例，输入流的 AVPacket 会丢给解码实例进行解码
    AVCodecContext *dec_ctx;
    //解码器信息
    AVCodec *dec;
    //从解码器上下文 解码出来的 AVFrame
    AVFrame *decoded_frame;
    //用来增加引用计数的，可能在调用 av_buffersrc_add_frame_flags() 之后，AVFrame 的引用技术会减一，所以需要先复制一份引用
    AVFrame *filter_frame; /* a ref of decoded_frame, to be sent to filters */

    //记录此流是什么时候开始处理的，主要是给 -re 选项使用的，模拟帧率速度，录制视频模拟直播用的
    int64_t       start;     /* time when read started */
    /* predicted dts of the next packet read for this stream or (when there are
     * several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */
    //下一帧的解码时间，这是通过计算得到的预估值，如果读取出来的 AVPacket 没有 dts ，会用这个值代替
    int64_t       next_dts;
    //最近一次从 av_read_frame() 读出来的 AVPacket 的 dts
    int64_t       dts;       ///< dts of the last packet read for this stream (in AV_TIME_BASE units)

    // 下一个 AVFrame的pts，通过当前AVFrame的 pts 加上 duration 计算出来的，应该是给一些没有 pts 值的 AVFrame 用的
    int64_t       next_pts;  ///< synthetic pts for the next decode frame (in AV_TIME_BASE units)
    // 最近一次从解码器里面解码出来的 AVFrame 的 pts，记录这个值主要是给 send_filter_eof() 用的，防止回滚
    int64_t       pts;       ///< current pts of the decoded frame  (in AV_TIME_BASE units)
    //在一些流格式，例如 TS，会有时间戳环回的问题，int64 是有大小限制的，超过这个大小会环回，ffmpeg.exe 需要处理环回的逻辑
    int           wrap_correction_done;

    //专门给音频帧转换时间基用的，因为音频的连续性太强，如果有些许误差需要保存下来，在下次转换的时候进行补偿
    int64_t filter_in_rescale_delta_last;

    // 从 av_read_frame() 读出来的 AVPacket 的最小 pts ，不一定是第一帧的 pts，因为命令行参数通过 -ss 选项，指定从哪里开始处理
    int64_t min_pts; /* pts with the smallest value in a current stream */
    //从 av_read_frame() 读出来的 AVPacket 的最大 pts ，不一定是最后一帧的 pts，因为命令行参数通过 -t 选项，指定只处理多久的时长
    int64_t max_pts; /* pts with the higher value in a current stream */

    // when forcing constant input framerate through -r,
    // this contains the pts that will be given to the next decoded frame
    //给 -r 选项用的
    int64_t cfr_next_pts;

    //记录的是最近一次解码出来的 AVFrame 里面的 nb_samples
    int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */

    //默认值是 1.0，可通过命令行选项 -itsscale 进行改变，主要作用是将 pts ，dts 进行放大，放大的方法是乘以 ts_scale
    double ts_scale;
    //标记是不是已经读取到属于该流的第一个 AVPacket
    int saw_first_ts;
    //从 OptionsContext 里面转移过来的解码器参数
    AVDictionary *decoder_opts;
    //命令行选项 -r 的值
    AVRational framerate;               /* framerate forced with -r */
    //命令行选项 -top 的值，好像是给隔行扫描视频用的
    int top_field_first;
    //命令行选项 -guess_layout_max 的值，猜测的最大的声道布局
    int guess_layout_max;

    //命令行选项 -autorotate 的值，是否插入纠正旋转的 filter 滤镜，
    //有些视频的画面中途会上下旋转，设置这个选项可以自动纠正旋转，不会上下颠倒
    int autorotate;

    int fix_sub_duration;
    struct { /* previous decoded subtitle and related variables */
        int got_output;
        int ret;
        AVSubtitle subtitle;
    } prev_sub;

    struct sub2video {
        int64_t last_pts;
        int64_t end_pts;
        AVFifoBuffer *sub_queue;    ///< queue of AVSubtitle* before filter init
        AVFrame *frame;
        int w, h;
        unsigned int initialize; ///< marks if sub2video_update should force an initialization
    } sub2video;

    //用 Find Useage 找不到使用这个字段的代码，所以是没用的字段
    int dr1;

    /* decoded data from this stream goes into all those filters
     * currently video and audio only */
    //跟int nb_filters，输入流绑定的入口滤镜，可以是绑定多个入口滤镜的，输入流解码出来的 AVFrame 会往多个入口滤镜发送
    InputFilter **filters;
    int        nb_filters;

    //命令行选项 -reinit_filter 的值，0 代表输入信息如果产生变化也不重新初始化 FilterGraph，
    // 输入信息产生变化是指 解码出来的 AVFrame 的宽高中途变大或者变小之类的。reinit_filters 默认是 -1，
    // 就是会重新初始化 FilterGraph
    int reinit_filters;

    /* hwaccel options */
    enum HWAccelID hwaccel_id;
    enum AVHWDeviceType hwaccel_device_type;
    char  *hwaccel_device;
    enum AVPixelFormat hwaccel_output_format;

    /* hwaccel context */
    void  *hwaccel_ctx;
    void (*hwaccel_uninit)(AVCodecContext *s);
    int  (*hwaccel_get_buffer)(AVCodecContext *s, AVFrame *frame, int flags);
    int  (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
    enum AVPixelFormat hwaccel_pix_fmt;
    enum AVPixelFormat hwaccel_retrieved_pix_fmt;
    AVBufferRef *hw_frames_ctx;

    /* stats */
    // combined size of all the packets read
    //从 av_read_frame() 读出来的 AVPacket 的 size 的总和。也就是统计当前流一共读出出来了多少字节的数据
    uint64_t data_size;
    /* number of packets successfully read for this stream */
    //从 av_read_frame() 读出来的 AVPacket 数量的总和
    uint64_t nb_packets;
    // number of frames/samples retrieved from the decoder
    //从解码器解码出来的 AVFrame 的数量
    uint64_t frames_decoded;
    uint64_t samples_decoded;

    int64_t *dts_buffer;
    int nb_dts_buffer;

    //只要解码器已经解码出一帧 AVFrame，这个字段就会置为 1
    int got_output;
} InputStream;

typedef struct InputFile {
    AVFormatContext *ctx; //容器上下文，也叫容器实例
    //代表输入文件是否已经读取结束。不一定是读到文件结尾才是读取结束。
    //命令行参数也可以用 -t 选项指定输入文件的时长，指定只读取，只处理这么长时间的内容
    int eof_reached;      /* true if eof reached */
    // 代表上一次 av_read_frame() 函数是否返回了 EAGAIN 错误码。
    // 当 av_read_frame() 函数返回 EAGAIN ，OutputStream 的 unavailable 字段就会置为 1， 
    // unavailable 字段代表输出流暂时不可用，只是暂停.即休眠一段时间,然后再读取
    int eagain;           /* true if last read attempt returned EAGAIN */
    //输入文件的第一个流在 input_streams[] 数组的位置索引
    //input_streams 是一个全局变量数组，存放所有的输入文件的所有的输入流
    int ist_index;        /* index of first stream in input_streams */
    //此输入文件应该被循环读取多少次.如命令设置 ffmpeg -stream_loop 2 -i juren.mp4 juren.flv
    int loop;             /* set number of times input stream should be looped */
    //在循环读取的时候用的，开始第二次循环之前，会把 duration 设置为流的时长，
    //第二次循环的时候 pts 会加上 duration，这样时间就拼在一起了
    int64_t duration;     /* actual duration of the longest stream in a file
                             at the moment when looping happens */
    // duration 字段的时间基
    AVRational time_base; /* time base of the duration */
    //直接把命令行选项的 -itsoffset 5 赋值过来的，会设置成 5 ，只在一个地方被用到
    int64_t input_ts_offset;

    int64_t ts_offset;
    //记录的是最近一次读取出来的 AVPacket 的 dts 时间
    int64_t last_ts;
    // 命令行的 -ss 选项赋值过来的参数，代表从输入文件的 第 N 秒开始处理。
    // 时间基是 AV_TIME_BASE -ss 选项是通过 trim 滤镜实现的，trim 滤镜可以指定从那个时间点开始裁剪
    int64_t start_time;   /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
    //记录seek 的时间
    int seek_timestamp;
    //输入文件的最大处理时长，例如一个文件3分钟，可以通过命令行选项 -t 60 指定只处理 1分钟的时长。
    //限制时长是通过 trim 滤镜实现的
    int64_t recording_time;
    // 输入文件实际的流数量，可能会比 ctx.nb_streams 大，因为有些文件，
    //可能 av_read_frame() 会读出其他流，应该是 ctx.nb_streams 记录的大小不准确
    int nb_streams;       /* number of stream that ffmpeg is aware of; may be different
                             from ctx.nb_streams if new streams appear during av_read_frame() */
    //等于 nb_streams 减去 ctx.nb_streams，实际上就是动态读取到的流数量
    int nb_streams_warn;  /* number of streams that the user was warned of */
    //命令行参数 -re 赋值过来的，代表按照帧率去输出，可以模拟现场直播
    int rate_emu;
    int accurate_seek;

#if HAVE_THREADS
    AVThreadMessageQueue *in_thread_queue;
    pthread_t thread;           /* thread reading from this file */
    int non_blocking;           /* reading packets from the thread should not block */
    int joined;                 /* the thread has been joined */
    int thread_queue_size;      /* maximum number of queued packets */
#endif
} InputFile;

enum forced_keyframes_const {
    FKF_N,
    FKF_N_FORCED,
    FKF_PREV_FORCED_N,
    FKF_PREV_FORCED_T,
    FKF_T,
    FKF_NB
};

#define ABORT_ON_FLAG_EMPTY_OUTPUT        (1 <<  0)
#define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 <<  1)

extern const char *const forced_keyframes_const_names[];

typedef enum {
    ENCODER_FINISHED = 1,
    MUXER_FINISHED = 2,
} OSTFinished ;

//单个输出流的管理器
typedef struct OutputStream {
    int file_index;          /* file index *///全局变量 output_files 数组里面的索引值
    //oc->nb_streams - 1，索引是容器里面最大的那个流的索引
    int index;               /* stream index in the output file */
    //此输出流对应的输入流，-1 代表没有对应的输入流，通常是因为使用了滤镜，把多个输入流混合成一个输出流。
    //source_index 是 input_streams 数组里面的索引值
    int source_index;        /* InputStream index */
    //从容器上下文里面复制过来的
    AVStream *st;            /* stream in the output file */
    //此输出流的数据需不需要进行编码操作。只要命令行参数没使用 copy 选项，都是需要进行编码操作的
    int encoding_needed;     /* true if encoding needed for this stream */
    //统计一共往编码器输入了多少个 AVFrame
    int frame_number;
    /* input pts and corresponding output pts
       for A/V sync */
    struct InputStream *sync_ist; /* input stream to sync against */
    //输入给编码器的每一个 AVFrame 的 pts 都是用 sync_opts 来赋值的
    int64_t sync_opts;       /* output frame counter, could be changed to some true timestamp */ // FIXME look at frame_number
    /* pts of the first frame encoded for this stream, used for limiting
     * recording time */
    // 此流的第一个 AVFrame 的 pts，可能是音频的pts，也可能是视频的pts。
    //主要是用来限制输出时长的，配合那个 recording time 字段使用
    int64_t first_pts;
    /* dts of the last packet sent to the muxer */
    //最近一次写入容器文件的 AVPacket 的 dts
    int64_t last_mux_dts;
    // the timebase of the packets sent to the muxer
    //复用器的时间基，也就是 AVPacket 写入文件的时候，它的 pts 需要换成 mux_timebase 的时间基
    AVRational mux_timebase;
    //编码器的时间基，AVFrame 发送给编码器之前，AVFrame 的 pts 需要换成 enc_timebase的时间基
    AVRational enc_timebase;

    //字节流滤镜实例，可以用命令行参数 -bsf 指定，例如 -bsf:vh264_mp4toannexb 这个参数的作用
    //是将MP4中的H. 264 数据转换为H. 264AnnexB 标准的编码， AnnexB标准的编码常见于实时传输流中
    AVBSFContext            *bsf_ctx;

    //编码器实例，编码器上下文
    AVCodecContext *enc_ctx;
    //编码器参数，主要用于 stream copy 的场景，其实是直接复制的输入流的编码参数
    AVCodecParameters *ref_par; /* associated input codec parameters with encoders options applied */
    //编码器信息
    AVCodec *enc;
    //命令行选项 -frames 的值，限制此输出流最多输出多少个 AVFrame
    int64_t max_frames;
    //从 FilterGraph 出口滤镜里面读出来的 AVFrame
    AVFrame *filtered_frame;
    //指向上一次发送给 编码器的 AVFrame
    AVFrame *last_frame;
    //用于帧率变换的逻辑，代表最近的一个 AVFrame 是否丢弃了
    int last_dropped;
    //用于 帧率变换的逻辑
    int last_nb0_frames[3];
    // 硬件编码上下文
    void  *hwaccel_ctx;

    /* video only */
    //帧率，通过 -r 选项指定的
    AVRational frame_rate;
    //此输出流是否是常量帧率
    int is_cfr;
    //强制指定帧率，通过 -force_fps 选项指定的，是个布尔值
    int force_fps;
    //隔行扫描视频用的
    int top_field_first;
    int rotate_overridden;
    double rotate_override_value;

    //这是一个比例，显示播放的时候用这个比例拉伸 width 跟height 作为显示窗口，图像播放就不会扭曲了
    AVRational frame_aspect_ratio;

    /* forced key frames */
    //视频强制关键帧字段
    int64_t forced_kf_ref_pts;
    int64_t *forced_kf_pts;
    int forced_kf_count;
    int forced_kf_index;
    char *forced_keyframes;
    AVExpr *forced_keyframes_pexpr;
    double forced_keyframes_expr_const_values[FKF_NB];

    /* audio only */
    //给命令行的 map 语法用的
    int *audio_channels_map;             /* list of the channels id to pick from the source stream */
    int audio_channels_mapped;           /* number of channels in audio_channels_map */
    
    //日志前缀
    char *logfile_prefix;
    FILE *logfile;

    //输出流关联的出口滤镜，输出流需要从这个出口滤镜读取 AVFrame
    OutputFilter *filter;
    //可能是 filters 或者 filters_script 的值，如果这两个字段都没有值，那就是空滤镜， anull 跟 null
    char *avfilter;
    // 命令行 -filter 指定的滤镜字符串
    char *filters;         ///< filtergraph associated to the -filter option
    // 滤镜脚本文件的名称以及路径。这是把滤镜的字符串放在一个文件里面，从文件里面读取
    char *filters_script;  ///< filtergraph script associated to the -filter_script option

    AVDictionary *encoder_opts;
    AVDictionary *sws_dict;
    AVDictionary *swr_opts;
    AVDictionary *resample_opts;
    //命令行选项 -apad 的值
    char *apad;
    //默认是 0，有两个值
    OSTFinished finished;        /* no more packets should be written for this stream */
    //代表输出流不可用，可能只是暂时不可用，例如 av_read_frame() 从输入流返回 EAGAIN 错误码，
    //就会导致输出流暂时不可用，暂时不可用就会休眠一段时间
    int unavailable;                     /* true if the steram is unavailable (possibly temporarily) */
    //如果命令行使用了 copy ，这个字段会置为 1
    int stream_copy;

    // init_output_stream() has been called for this stream
    // The encoder and the bitstream filters have been initialized and the stream
    // parameters are set in the AVStream.
    //当前流是否已经被初始化了，只有初始化才能往容器里面写头部信息
    int initialized;

    //代表没有找到输出流对应的输入流，通常不会没找到，所以通常一直都是
    int inputs_done;

    //命令行 -attach 功能的字段，添加附件用的
    const char *attachment_filename;
    //命令行 -copyinkf 功能的字段
    int copy_initial_nonkeyframes;
    // 命令行 -copypriorss 功能的字段
    int copy_prior_start;
    //命令行 -disposition 功能的字段
    char *disposition;
    //命令行如果指定了 pix_fmt，keep_pix_fmt 这个字段的值会是 1
    int keep_pix_fmt;

    /* stats */
    // combined size of all the packets written
    //由该流所有的 AVPacekt 的 data_size 字段累计起来的
    uint64_t data_size;
    // number of packets send to the muxer
    //已经有多少个 AVPacket 被送进去了 muxer
    uint64_t packets_written;
    // number of frames/samples sent to the encoder
    //已经有多少个 AVFrame 被送进去了 编码器
    uint64_t frames_encoded;
    uint64_t samples_encoded;

    /* packet quality factor */
    //视频的编码器的编码质量
    int quality;

    //muxing_queue 队列的大小，如果视频输出流已经初始化完成了，
    //当音频输出流没有初始化完成的时候，这时候是不能 用 avformat_write_header() 写入 头信息到容器
    int max_muxing_queue_size;

    /* the packets are buffered here until the muxer is ready to be initialized */
    //muxing_queue 队列
    AVFifoBuffer *muxing_queue;

    /* packet picture type */
    //最近一次编码器输出的 AVPacket 的图片类型，例如 I 帧， P帧，还是 B 帧
    int pict_type;

    /* frame encode sum of squared error values */
    //统计编码错误用的
    int64_t error[4];
} OutputStream;

//单个输出文件的管理器
typedef struct OutputFile {
    AVFormatContext *ctx; //容器上下文，也叫容器实例
    AVDictionary *opts; //容器格式的参数，是从 OptionsContext 里面 的 OptionGroup 的 format_opts 复制过来的
    //输出文件的第一个流在 output_streams 数组里面的索引，output_streams 数组是一个全局变量，里面包含所有输出文件的所有输出流
    int ost_index;       /* index of the first stream in output_streams */
    //命令行选项 -t 的值，设置输出文件的时长，单位是微秒，具体的功能是通过 trim 滤镜来实现的
    int64_t recording_time;  ///< desired length of the resulting file in microseconds == AV_TIME_BASE units
    //标记输出文件的开始时间.例如一个输入文件本来是 6 分钟的，
    //可以用 -ss 120 指定 start_time，这样，输出文件就会裁剪成 第 2 ~ 6分钟 的视频，前面 2 分钟丢弃。
    int64_t start_time;      ///< start time in microseconds == AV_TIME_BASE units
    //限制输出文件的大小，一旦达到这个大小，输出文件立即结束
    uint64_t limit_filesize; /* filesize limit expressed in bytes */
    //命令行选项 -shortest 的值，当最短的输出流结束的时候，整个文件就结束了，
    //例如一个输出文件里面有 音频流 跟 视频流，视频流 3 分钟，音频流 5 分钟。如果启用了这个选项，音频流就会被裁剪成 3 分钟
    int shortest;
    //是否已经调用了 avformat_write_header() 函数，往输出文件写入了头部信息
    int header_written;
} OutputFile;

extern InputStream **input_streams;
extern int        nb_input_streams;
extern InputFile   **input_files;
extern int        nb_input_files;

extern OutputStream **output_streams;
extern int         nb_output_streams;
extern OutputFile   **output_files;
extern int         nb_output_files;

extern FilterGraph **filtergraphs;
extern int        nb_filtergraphs;

extern char *vstats_filename;
extern char *sdp_filename;

extern float audio_drift_threshold;
extern float dts_delta_threshold;
extern float dts_error_threshold;

extern int audio_volume;
extern int audio_sync_method;
extern int video_sync_method;
extern float frame_drop_threshold;
extern int do_benchmark;
extern int do_benchmark_all;
extern int do_deinterlace;
extern int do_hex_dump;
extern int do_pkt_dump;
extern int copy_ts;
extern int start_at_zero;
extern int copy_tb;
extern int debug_ts;
extern int exit_on_error;
extern int abort_on_flags;
extern int print_stats;
extern int qp_hist;
extern int stdin_interaction;
extern int frame_bits_per_raw_sample;
extern AVIOContext *progress_avio;
extern float max_error_rate;
extern char *videotoolbox_pixfmt;

extern int filter_nbthreads;
extern int filter_complex_nbthreads;
extern int vstats_version;

extern const AVIOInterruptCB int_cb;

extern const OptionDef options[];
extern const HWAccel hwaccels[];
#if CONFIG_QSV
extern char *qsv_device;
#endif
extern HWDevice *filter_hw_device;


void term_init(void);
void term_exit(void);

void reset_options(OptionsContext *o, int is_input);
void show_usage(void);

void opt_output_file(void *optctx, const char *filename);

void remove_avoptions(AVDictionary **a, AVDictionary *b);
void assert_avoptions(AVDictionary *m);

int guess_input_channel_layout(InputStream *ist);

enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *avctx, AVCodec *codec, enum AVPixelFormat target);
void choose_sample_fmt(AVStream *st, AVCodec *codec);

int configure_filtergraph(FilterGraph *fg);
int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out);
void check_filter_outputs(void);
int ist_in_filtergraph(FilterGraph *fg, InputStream *ist);
int filtergraph_is_simple(FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
int init_complex_filtergraph(FilterGraph *fg);

void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);

int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);

int ffmpeg_parse_options(int argc, char **argv);

int videotoolbox_init(AVCodecContext *s);
int qsv_init(AVCodecContext *s);

HWDevice *hw_device_get_by_name(const char *name);
int hw_device_init_from_string(const char *arg, HWDevice **dev);
void hw_device_free_all(void);

int hw_device_setup_for_decode(InputStream *ist);
int hw_device_setup_for_encode(OutputStream *ost);
int hw_device_setup_for_filter(FilterGraph *fg);

int hwaccel_decode_init(AVCodecContext *avctx);

#endif /* FFTOOLS_FFMPEG_H */
